# !/usr/bin/env python
# -*-coding:utf-8 -*-

"""
# File       : process_finite_state_machines.py
# Time       ：2024/5/16 19:28
# Author     ：YangYong
# version    ：python 3.10
# Description：流程状态机操作
"""
import logging
from datetime import datetime

from transitions import Machine

from apis.orderlines.models import ProcessInstance
from apis.orderlines.schema.process_schema import ProcessInstanceSchema
from orderlines.utils.exceptions import StateChangeException
from orderlines.utils.orderlines_enum import ProcessStates
from public.base_model import session_scope
from public.logger import logger

logging.getLogger('transitions').setLevel(logging.ERROR)
logging.getLogger('transitions').setLevel(logging.WARNING)


class ProcessInstanceState:
    process_states = ['SUCCESS', 'FAILURE', 'STOP', 'PENDING', 'RUNNING', 'PAUSED', 'TIMEOUT']

    def __init__(self, process_instance_id: str):
        self.state = None
        self.process_instance_id = process_instance_id

        self.table_orm = ProcessInstance
        self.table_schema = ProcessInstanceSchema
        self.machine = Machine(self, self.process_states, initial=self.get_init_state(), name='process')
        self.machine.add_transition(
            trigger='start',
            source='PENDING',
            dest='RUNNING',
            conditions='can_start',
            after='on_running'
        )
        self.machine.add_transition(
            trigger='pause',
            source=['PENDING', 'RUNNING'],
            dest='PAUSED',
            conditions='can_pause',
            after='on_paused'
        )
        self.machine.add_transition(
            trigger='stop',
            source=['PENDING', 'RUNNING'],
            dest='STOP',
            after='on_stop'
        )
        self.machine.add_transition(
            trigger='recover',
            source=['PAUSED'],
            dest='RUNNING',
            conditions='can_recover',
            after='on_running'
        )
        self.machine.add_transition(
            trigger='timeout',
            source=['RUNNING', 'PENDING'],
            dest='TIMEOUT',
            after='on_timeout'
        )
        self.machine.add_transition(
            trigger='failure',
            source=['RUNNING'],
            dest='FAILURE',
            after='on_failure'
        )
        self.machine.add_transition(
            trigger='success',
            source=['RUNNING'],
            dest='SUCCESS',
            after='on_success'
        )
        self.machine.add_transition(
            trigger='restart',
            source=['SUCCESS', 'FAILURE', 'STOP', 'PAUSED', 'TIMEOUT'],
            dest='RUNNING',
            conditions='can_restart',
            after='on_running'
        )

    def get_init_state(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).first()
            return self.table_schema().dump(obj).get('process_status') or ProcessStates.grey.value

    def can_start(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.process_status == ProcessStates.grey.value,
                self.table_orm.active == 1
            ).first()
        if not obj:
            logger.error(f'流程状态机——无法启动:{self.process_instance_id}')
            raise StateChangeException(f'流程状态机——当前没有任务实例无法运行:{self.process_instance_id}')
        return obj is not None

    def can_pause(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.process_status == ProcessStates.blue.value,
                self.table_orm.active == 1
            ).first()
        if not obj:
            logger.error(f'流程状态机——无法暂停:{self.process_instance_id}')
            raise StateChangeException(f'流程状态机——无法暂停:{self.process_instance_id}')
        return obj is not None

    def can_recover(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.process_status == ProcessStates.purple.value,
                self.table_orm.active == 1
            ).first()
        if not obj:
            logger.error(f'流程状态机——无法恢复:{self.process_instance_id}')
            raise StateChangeException(f'流程状态机——无法恢复:{self.process_instance_id}')
        return obj is not None

    def can_restart(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.process_status not in ['SUCCESS', 'FAILURE', 'STOP', 'PAUSED', 'TIMEOUT'],
                self.table_orm.active == 1
            ).first()
        if not obj:
            logger.error(f'流程状态机——无法重启:{self.process_instance_id}')
            raise StateChangeException(f'流程状态机——无法重启:{self.process_instance_id}')
        return obj is not None

    def can_timeout(self):
        with session_scope() as session:
            obj = session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.process_status in ['RUNNING', 'PENDING'],
                self.table_orm.active == 1
            ).first()
        if not obj:
            logger.error(f'流程状态机——无法超时:{self.process_instance_id}')
            raise StateChangeException(f'流程状态机——无法超时:{self.process_instance_id}')
        return obj is not None

    def on_running(self):
        with session_scope() as session:
            process_instance_info = {'process_status': ProcessStates.blue.value}
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()

    def on_stop(self):
        process_instance_info = {
            'process_status': ProcessStates.yellow.value,
            'end_time': datetime.now().strftime('%Y-%m-%d %H-%M-%S')
        }
        with session_scope() as session:
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()

    def on_success(self):
        process_instance_info = {
            'process_status': ProcessStates.green.value,
            'end_time': datetime.now().strftime('%Y-%m-%d %H-%M-%S')
        }
        with session_scope() as session:
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()

    def on_failure(self, error_info):
        process_instance_info = {
            'process_status': ProcessStates.red.value,
            'end_time': datetime.now().strftime('%Y-%m-%d %H-%M-%S'),
            'process_error_info': error_info
        }
        with session_scope() as session:
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()

    def on_paused(self):
        process_instance_info = {
            'process_status': ProcessStates.purple.value,
            'end_time': datetime.now().strftime('%Y-%m-%d %H-%M-%S'),
            'process_error_info': {'notice': '流程停止'}
        }
        with session_scope() as session:
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()

    def on_timeout(self, message=None):
        if message is None:
            message = '流程超时'
        if isinstance(message, str):
            message = {'notice': message}
        process_instance_info = {
            'process_status': ProcessStates.orange.value,
            'end_time': datetime.now().strftime('%Y-%m-%d %H-%M-%S'),
            'process_error_info': message
        }
        with session_scope() as session:
            session.query(self.table_orm).filter(
                self.table_orm.process_instance_id == self.process_instance_id,
                self.table_orm.active == 1
            ).update(process_instance_info)
            session.commit()


    def trigger_chance(self, trigger_methods, *args, **kwargs):
        func = getattr(self, trigger_methods)
        if args or kwargs:
            return func(*args, **kwargs)
        else:
            return func()
